/*
* Copyright (c) 2012 Kyle Hayward
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once

namespace OptixDotNet 
{
	/// <summary>
	/// Enum used for declaring which D3D API a D3DBuffer or D3DTextureSampler will be created from
	/// </summary>
	public enum class D3DApi
	{
		None = 0,
		Direct3D9,
		Direct3D10,
		Direct3D11
	};

	ref class Buffer;
	/// <summary>
	/// Collection of formats used for reading and writing data to <see cref="OptixDotNet::Buffer">Buffers</see> 
	/// and <see cref="OptixDotNet::Variable">Variables</see>.
	/// </summary>
	public enum class Format
	{
		Unknown	= RT_FORMAT_UNKNOWN,
		Float	= RT_FORMAT_FLOAT,
		Float2	= RT_FORMAT_FLOAT2,
		Float3	= RT_FORMAT_FLOAT3,
		Float4	= RT_FORMAT_FLOAT4,
		Byte	= RT_FORMAT_BYTE,
		Byte2	= RT_FORMAT_BYTE2,
		Byte3	= RT_FORMAT_BYTE3,
		Byte4	= RT_FORMAT_BYTE4,
		UByte	= RT_FORMAT_UNSIGNED_BYTE,
		UByte2	= RT_FORMAT_UNSIGNED_BYTE2,
		UByte3	= RT_FORMAT_UNSIGNED_BYTE3,
		UByte4	= RT_FORMAT_UNSIGNED_BYTE4,
		Short	= RT_FORMAT_SHORT,
		Short2	= RT_FORMAT_SHORT2,
		Short3	= RT_FORMAT_SHORT3,
		Short4	= RT_FORMAT_SHORT4,
		UShort	= RT_FORMAT_UNSIGNED_SHORT,
		UShort2	= RT_FORMAT_UNSIGNED_SHORT2,
		UShort3	= RT_FORMAT_UNSIGNED_SHORT3,
		UShort4	= RT_FORMAT_UNSIGNED_SHORT4,
		Int		= RT_FORMAT_INT,
		Int2	= RT_FORMAT_INT2,
		Int3	= RT_FORMAT_INT3,
		Int4	= RT_FORMAT_INT4,
		UInt	= RT_FORMAT_UNSIGNED_INT,
		UInt2	= RT_FORMAT_UNSIGNED_INT2,
		UInt3	= RT_FORMAT_UNSIGNED_INT3,
		UInt4	= RT_FORMAT_UNSIGNED_INT4,
		User	= RT_FORMAT_USER
	};

	public enum class Type
	{
		Unknown				= RT_OBJECTTYPE_UNKNOWN,
		Group				= RT_OBJECTTYPE_GROUP,
		GeometryGroup		= RT_OBJECTTYPE_GEOMETRY_GROUP,
		Transform			= RT_OBJECTTYPE_TRANSFORM,
		Selector			= RT_OBJECTTYPE_SELECTOR,
		GeometryInstance	= RT_OBJECTTYPE_GEOMETRY_INSTANCE,
		Buffer				= RT_OBJECTTYPE_BUFFER,
		TextureSampler		= RT_OBJECTTYPE_TEXTURE_SAMPLER,
		Object				= RT_OBJECTTYPE_OBJECT,

		Matrix2x2			= RT_OBJECTTYPE_MATRIX_FLOAT2x2,
		Matrix2x3			= RT_OBJECTTYPE_MATRIX_FLOAT2x3,
		Matrix2x4			= RT_OBJECTTYPE_MATRIX_FLOAT2x4,
		Matrix3x2 			= RT_OBJECTTYPE_MATRIX_FLOAT3x2,
		Matrix3x3 			= RT_OBJECTTYPE_MATRIX_FLOAT3x3,
		Matrix3x4 			= RT_OBJECTTYPE_MATRIX_FLOAT3x4,
		Matrix4x2 			= RT_OBJECTTYPE_MATRIX_FLOAT4x2,
		Matrix4x3 			= RT_OBJECTTYPE_MATRIX_FLOAT4x3,
		Matrix4x4 			= RT_OBJECTTYPE_MATRIX_FLOAT4x4,

		Float				= RT_OBJECTTYPE_FLOAT,
		Float2				= RT_OBJECTTYPE_FLOAT2,
		Float3				= RT_OBJECTTYPE_FLOAT3,
		Float4				= RT_OBJECTTYPE_FLOAT4,
		Int					= RT_OBJECTTYPE_INT,
		Int2				= RT_OBJECTTYPE_INT2,
		Int3				= RT_OBJECTTYPE_INT3,
		Int4				= RT_OBJECTTYPE_INT4,
		UInt				= RT_OBJECTTYPE_UNSIGNED_INT,
		UInt2				= RT_OBJECTTYPE_UNSIGNED_INT2,
		UInt3				= RT_OBJECTTYPE_UNSIGNED_INT3,
		UInt4				= RT_OBJECTTYPE_UNSIGNED_INT4,
		User				= RT_OBJECTTYPE_USER
	};
	
	ref class Context;

	public ref class OptixNode abstract
	{
	internal:
		OptixNode();
		OptixNode( Context^ context );
		virtual ~OptixNode() { }

		virtual void Validate() = 0;
		virtual void Destroy() = 0;

		void CheckError( RTresult result );

		Context^ mContext;
	};

	public ref class DataNode abstract : public OptixNode
	{
	internal:
		DataNode( Context^ context ) : OptixNode( context ) { }

		virtual RTobject ObjectPtr() = 0;
	};

	public ref class ContainerNode abstract : DataNode
	{
	internal:
		ContainerNode( Context^ context ) : DataNode( context ) { }

		ContainerNode^ ConstructContainerNodeFromType( Type type, RTobject object );
	};

	/// <summary>
	/// Contains a collection of hierarchy Nodes.
	/// </summary>
	generic<typename T> where T : ref class
	public ref class NodeCollection
	{
	internal:
		NodeCollection()
		{
			Container = nullptr;
		}
		NodeCollection( IContainerNode<T>^ container )
		{
			Container = container;
		}
	public:

		/// <summary>
		/// Gets or sets the number of children in the collection
        /// </summary>
		property unsigned int Count
		{
			inline unsigned int get()
			{
				return Container->ChildCount;
			}
			inline void set( unsigned int count )
			{
				Container->ChildCount = count;
			}
		}

		/// <summary>
		/// Gets or sets a local node at index [index] and attaches it to the parent container
        /// </summary>
		property T default[int]
		{
			inline T get ( int index )
			{
				return Container->ChildNodes[index];
			}
			inline void set( int index, T v )
			{
				Container->ChildNodes[index] = v;
			}
		}

	internal:
		property IContainerNode<T>^ Container;
	};
}